#pragma once
#include <type_traits>
#include <iterator>

namespace impl
{
template <typename T>
struct is_iterator
{
    static char test(...);

    template <typename U,
              typename = typename std::iterator_traits<U>::difference_type,
              typename = typename std::iterator_traits<U>::pointer,
              typename = typename std::iterator_traits<U>::reference,
              typename = typename std::iterator_traits<U>::value_type,
              typename = typename std::iterator_traits<U>::iterator_category>
    static long test(U&&);

    using type = decltype(test(std::declval<T>()));
    static constexpr bool value = sizeof(type) != 1;
};

template <typename T>
struct is_container
{
    template <typename A,
              typename = decltype(std::declval<A>().begin()),
              typename = decltype(std::declval<A>().end()),
              typename = decltype(std::declval<A>().size()),
              typename = typename A::allocator_type,
              typename = typename A::const_iterator,
              typename = typename A::value_type>
    static constexpr char test(A&&);

    static constexpr long test(...);

    using type = decltype(test(std::declval<T>()));
    static constexpr bool value = sizeof(type) == 1;
};
}  // namespace impl